package org.terramagnet.mvcframework.http.signature.impl;

import org.terramagnet.mvcframework.http.Connection;
import org.terramagnet.mvcframework.http.signature.SignException;
import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.TreeSet;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * md5 签名逻辑.
 *
 * @author lip
 */
public class Md5SignatureAlgorithm extends SortedSignatureAlgorithm {

    private final Logger logger = LoggerFactory.getLogger(Md5SignatureAlgorithm.class);

    @Override
    public String signSortedFields(TreeSet<Connection.KeyVal> parameters) {
        StringBuilder builder = new StringBuilder();
        for (Connection.KeyVal parameter : parameters) {
            builder.append(parameter.key())
                .append("=")
                .append(parameter.value())
                .append("&");
        }
        if (!parameters.isEmpty()) {
            builder.deleteCharAt(builder.length() - 1);
        }
        builder.append(secret());
        logger.trace("before sign: {}", builder);
        try {
            MessageDigest instance = MessageDigest.getInstance("MD5");
            byte[] digest = instance.digest(builder.toString().getBytes("UTF-8"));
            String hex = new String(encodeHex(digest));
            logger.trace("after sign: {}", hex);
            return hex;
        } catch (NoSuchAlgorithmException ex) {
            throw new SignException(ex);
        } catch (UnsupportedEncodingException ex) {
            throw new SignException(ex);
        }
    }

    /**
     * Converts an array of bytes into an array of characters representing the
     * hexadecimal values of each byte in order. The returned array will be
     * double the length of the passed array, as it takes two characters to
     * represent any given byte.
     *
     * <p>
     * copied from commons-codec</p>
     *
     * @param data a byte[] to convert to Hex characters
     * @param toDigits the output alphabet
     * @return A char[] containing hexadecimal characters
     */
    private static char[] encodeHex(byte[] data) {
        int l = data.length;
        char[] out = new char[l << 1];
        // two characters form the hex value.
        for (int i = 0, j = 0; i < l; i++) {
            out[j++] = DIGITS_LOWER[(0xF0 & data[i]) >>> 4];
            out[j++] = DIGITS_LOWER[0x0F & data[i]];
        }
        return out;
    }
    public static final char[] DIGITS_LOWER = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};

}
